﻿/*******************************************************************************
* File Name  : NFSession.cpp
* Description: This file contains the implementation of the NFSession class, which
*              manages the session with the client.
* Author     : gaoyi_ds
* Time       : 2024-07-30
* Version    : 1.0
* History:
* <author>      <time>      <version >  <desc>
*******************************************************************************/

#include "NFSession.h"
#include <NFServerComm/NFServerCommon/NFIServerMessageModule.h>
#include <NFComm/NFCore/NFServerTime.h>

#include <NFLogicConfig.h>
#include <NFlogEventHandler.h>
#include <NFPlayer.h>
#include <NFPlayerMgr.h>
#include <NFPlayerUtil.h>

#include "NFPacketSendUtil.h"
#include "NFRegisterCache.h"
#include "proto_access2zone.nanopb.h"
#include "proto_svr_common.nanopb.h"
#include "proto_svr_onlinesvr.pb.h"

// Default constructor
NFSession::NFSession()
{
    if (NFShmMgr::Instance()->GetCreateMode() == EN_OBJ_MODE_INIT)
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

// Destructor
NFSession::~NFSession()
{

}

// Initialize the session, return 0 for success
int NFSession::CreateInit()
{
    m_iPlayerObjID = 0;
    m_pPlayer = nullptr;

    m_iLoginChannel = 0;
    m_bClientVersionFlag = 0;
    m_tDelayStopTime = 0;

    m_bBusy = false;

    m_wClientSeq = 0;
    m_wServerSeq = 1;

    m_bPreRegist = false;
    m_iLoginPri = 0;
    m_iLoginZoneID = 0;

    m_bIsLogin = false;

    m_bIsRecruit = false;
    m_bAndriodCounted = false;
    m_bIOSCounted = false;
    m_iZoneAreaID = 0;
    m_bindulge = false;
    m_isRealName = false;
    m_isWillDelete = false;

    m_bLoginQueue = false;

    m_loginMirrorTime = 0;
    m_loginMirrorRoleId = 0;

    m_eSessionState = SESSION_STATE_NORMAL;
    m_targetSessionId = 0;
    m_targetSvrId = 0;
    return 0;
}

// Resume the initialization of the session, return 0 for success
int NFSession::ResumeInit()
{
    m_pPlayer = nullptr;
    return 0;
}

// Connect to the server, no return value indicates the operation is asynchronous
int NFSession::Connect(NFConnectionInfo *pstConnInfo)
{
    CHECK_NULL(0, pstConnInfo);
    SetConnInfo(pstConnInfo);
    m_stClientInfo.plat_type = (Proto_PlatType) m_stConnInfo.m_iDeviceType;
    if (!m_bAndriodCounted && !m_bIOSCounted)
    {
        switch (m_stClientInfo.plat_type)
        {
            case proto_ff::E_PLAT_ANDROID:
                m_bAndriodCounted = true;
                break;
            case proto_ff::E_PLAT_IOS:
                m_bIOSCounted = true;
                break;
            default:
                break;
        }
    }

    NFLogInfo(NF_LOG_DEFAULT, 0, " connect from {}, objid:", pstConnInfo->ShortDebugString(), GetGlobalId());

    //return message needed
    SetLastActiveTime(NF_ADJUST_TIMENOW());
    NFlogEventHandler::OnSessionCreateSuccess(this);
    return 0;
}

int NFSession::Connect(const proto_ff::NFConnectionInfo* pstConnInfo)
{
    NFConnectionInfo conn;
    conn.FromPb(*pstConnInfo);
    return Connect(&conn);
}

// Disconnect from the server, parameter iType indicates the type of disconnection, iReason indicates the reason
int NFSession::DisConnect(int iType, int iReason)
{
    NFPlayer *pPlayer = GetPlayer();

    if (pPlayer && pPlayer->GetClientSession() == this)
    {
        LOGPLAYER_INFO(pPlayer->GetRoleID(), "RoleID:" << pPlayer->GetRoleID() << " disconnect from " << m_stConnInfo.ShortDebugString()
                       << ", objid:" << GetGlobalId() << ".!type:" << iType << ";reason:" << iReason << ";player status:" << pPlayer->GetPlayerStatus());

        if (!pPlayer->GetRoleDetailSyned())
        {
            //直接logout
            NFPlayerUtil::LogoutPlayer(pPlayer);
            return 0;
        }

        switch (pPlayer->GetPlayerStatus())
        {
            case CACHE_STATUS_NONE:
            case CACHE_STATUS_ONLINE:
                m_stConnInfo.m_dwDisConnTime = NF_ADJUST_TIMENOW();
                m_stConnInfo.m_iDisConnType = iType;
                m_stConnInfo.m_iDisConnReason = iReason;
                pPlayer->SetPlayerStatus(CACHE_STATUS_OFFLINE);
                pPlayer->SetLastDiconnectTime(NF_ADJUST_TIMENOW());
                NFPlayerUtil::DisconnectPlayer(pPlayer, iType, iReason);
                break;
            case CACHE_STATUS_OFFLINE:
                //考虑客户端断线在等待期间,服务器此时也可能来踢人,此时的Logout要比客户端优先级别高,所以对应Logout时间要加快
                if (iType > m_stConnInfo.m_iDisConnType)
                {
                    m_stConnInfo.m_iDisConnType = iType;
                    m_stConnInfo.m_iDisConnReason = iReason;
                    m_stConnInfo.m_dwDisConnTime = NF_ADJUST_TIMENOW();
                    pPlayer->SetLastDiconnectTime(NF_ADJUST_TIMENOW());
                }
                break;
            default:
                break;
        }
    }
    else
    {
        //未绑定角色,直接删除
        if (iReason != 0)
        {
            LOGSVR_FATAL(" disconnect from " << m_stConnInfo.ShortDebugString() << ", objid:" << GetGlobalId() << " remove session. Reason:" << iReason);
        }
        SetWillDelete();
    }
    return 0;
}

// Get the device type, the return value is the device type code
int32_t NFSession::GetDeviceType() const
{
    return m_stConnInfo.m_iDeviceType;
}

// Get the zone ID, return value is the zone ID
int NFSession::GetZoneID()
{
    if (0 != m_iZoneAreaID)
    {
        return NFServerIDUtil::GetZoneIDFromZoneAreaID(m_iZoneAreaID);
    }
    else
    {
        return NFGlobalSystem::Instance()->GetGlobalPluginManager()->GetZoneID();
    }
}

// Initialize client information based on the login request
void NFSession::InitClientInfoByLoginReq(const proto_ff::Proto_CSLoginReq *pLoginReq)
{
    m_stClientInfo.FromPb(pLoginReq->client_info());
    if (m_stConnInfo.m_szOpenID.empty())
    {
        m_stConnInfo.m_szOpenID = pLoginReq->account_id().c_str();
    }
    m_stClientInfo.plat_type = (Proto_PlatType) m_stConnInfo.m_iDeviceType;

    m_iLoginPri = pLoginReq->login_pri();
    m_iLoginZoneID = pLoginReq->zone_id();
}

// Get debug information, the return value is the debug information string
std::string NFSession::GetDebugInfo() const
{
    return "";
}

// Bind a player to the session, the return value is 0 for success
int NFSession::BindPlayer(NFPlayer *pPlayer)
{
    if (pPlayer)
    {
        m_iPlayerObjID = pPlayer->GetGlobalId();
        m_pPlayer = pPlayer;
    }
    else
    {
        m_iPlayerObjID = INVALID_ID;
        m_pPlayer = pPlayer;
    }

    return 0;
}

// Get the player object bound to the session
NFPlayer *NFSession::GetPlayer()
{
    if (m_pPlayer)
    {
        return m_pPlayer;
    }

    m_pPlayer = NFPlayer::GetObjByGlobalId(m_iPlayerObjID);

    return m_pPlayer;
}

// Get the active player object in the session
NFPlayer *NFSession::GetActivePlayer()
{
    m_pPlayer = NFPlayerUtil::GetActivePlayer(m_iPlayerObjID);

    return m_pPlayer;
}

// Activate the session and check the message, the return value is the result of the check
int NFSession::ActiveAndCheck(uint16_t wMsgId, uint8_t bSubMsgId, uint32_t activeTime, uint16_t wClientSeq)
{
    SetLastActiveTime(activeTime);
    return 0;
}

// Handle the login process, the parameter is the login request information
int NFSession::Login(const proto_ff::Proto_CSLoginReq *pLoginReq)
{
    m_stConnInfo.m_bWorldType = NFGlobalSystem::Instance()->GetGlobalPluginManager()->GetWorldID(); // 不检验，玩家身上的worldtype就等于登录服务器配置的worldtype
    InitClientInfoByLoginReq(pLoginReq);

    m_bIsLogin = true;
    NFlogEventHandler::OnPlayerGetAccountInfoReq(this);
    LOGSVR_TRACE("\n" << pLoginReq->client_info().DebugString());

    return 0;
}

int NFSession::Login(const proto_ff::Proto_LoginClientInfo* pClientInfo, int loginPri, int loginZoneId)
{
    m_stConnInfo.m_bWorldType = NFGlobalSystem::Instance()->GetGlobalPluginManager()->GetWorldID(); // 不检验，玩家身上的worldtype就等于登录服务器配置的worldtype
    m_stClientInfo.FromPb(*pClientInfo);
    m_stClientInfo.plat_type = (Proto_PlatType) m_stConnInfo.m_iDeviceType;

    m_iLoginPri = loginPri;
    m_iLoginZoneID = loginZoneId;

    m_bIsLogin = true;
    NFlogEventHandler::OnPlayerGetAccountInfoReq(this);

    return 0;
}

// Get the player's role ID
uint64_t NFSession::GetRoleId()
{
    auto pPlayer = GetPlayer();
    if (pPlayer)
    {
        return pPlayer->GetRoleID();
    }

    return 0;
}

int NFSession::CheckPkgRate(int msg_id)
{
    CHECK_EXPR_RETURN(NFLogicConfig::Instance()->IsPacketRateCheck());

    int count = 0;
    int interval = 0;
    uint64_t roleID = 0;
    if (m_pPlayer)
    {
        roleID = m_pPlayer->GetRoleID();
    }

    m_stPkgMng.AddPkgStatistic(msg_id, roleID, m_stConnInfo.m_ullUin);
    int ret = m_stPkgMng.CheckPkgRate(msg_id, count, interval);
    if (ret)
    {
        LOGSVR_FATAL("pkg check and kick player:" << roleID << " uin:" << m_stConnInfo.m_ullUin << " openid:" <<
                GetAccountID() << " msgID:" << msg_id << " count:" << count << " interval:" << interval << " ret:" << ret);
        if (m_pPlayer)
        {
            NFPlayerUtil::KickPlayer(m_pPlayer, proto_ff::ERR_CODE_SEND_PACKET_FREQUENCY_FAST);
        }
        else
        {
            int iRetCode = SendConndStop();
            if (iRetCode)
                DisConnect(SESSION_DISCONN_FROM_SERVER, SESSION_DISCONN_MSG_MONITOR);
        }

        return -1;
    }

    return 0;
}

bool NFSession::IsFromQQGameCenter()
{
    return false;
}

bool NFSession::IsFromWXGameCenter()
{
    return false;
}

int NFSession::CloneSessionSeq(NFSession *pSession)
{
    CHECK_NULL(0, pSession);

    m_wClientSeq = pSession->GetClientSeq();
    m_wServerSeq = pSession->GetServerSeq();

    return 0;
}

int NFSession::OnPreRegist(bool bSet)
{
    m_bPreRegist = bSet;
    if (bSet)
    {
        NFRegisterCache::Instance()->AddPreRegistNum();
    }
    else
    {
        NFRegisterCache::Instance()->DelPreRegistNum();
    }

    return 0;
}

void NFSession::SetWillDelete()
{
    m_iPlayerObjID = 0;
    m_pPlayer = nullptr;
    m_isWillDelete = true;
}

int NFSession::Check(uint16_t wMsgId, timeval *pstTimeStamp, uint16_t wClientSeq)
{
    return 0;
}

int NFSession::SendConndStop()
{
    return FindModule<NFIServerMessageModule>()->SendMsgToProxyServer(NF_ST_LOGIC_SERVER, GetConnBusId(), E_ACCESS2ZONE_STOP, proto_ff::Zone2AccessStop(), GetConnIndex());
}

int NFSession::LockLoginMirror(uint64_t roleId)
{
    m_loginMirrorTime = NF_ADJUST_TIMENOW();
    m_loginMirrorRoleId = roleId;
    LOG_INFO(GetGlobalId(), "LockLoginMirror roleId:{} time:{}", roleId, m_loginMirrorTime);
    return 0;
}

int NFSession::UnlockLoginMirror(int reason)
{
    LOG_INFO(GetGlobalId(), "UnlockLoginMirror roleId:{} time:{} reason:{}", m_loginMirrorRoleId, m_loginMirrorTime, reason);
    m_loginMirrorTime = 0;
    m_loginMirrorRoleId = 0;
    return 0;
}

bool NFSession::IsLockedLoginMirror()
{
    return m_loginMirrorTime != 0;
}

bool NFSession::IsLockedLoginMirrorExpire()
{
    if (m_loginMirrorTime >0 && NF_ADJUST_TIMENOW() > m_loginMirrorTime + 10)
    {
        return true;
    }
    return false;
}

int NFSession::SetSessionState(enuSessionState eState, int iTargetSessionId, int iTargetSvrId)
{
    m_eSessionState = eState;
    SetRouteInfo(iTargetSessionId, iTargetSvrId);
    return 0;
}

int NFSession::SetRouteInfo(int iTargetSessionId, int iTargetGameSvrId)
{
    m_targetSessionId = iTargetSessionId;
    m_targetSvrId = iTargetGameSvrId;
    return 0;
}

int NFSession::TransferCSMsgRes(uint32_t cmd, const google::protobuf::Message* pstMsgRes)
{
    proto_ff::Proto_SSZoneTranser2MirrorSession req;
    req.set_session_id(m_targetSessionId);
    req.set_msg_id(cmd);
    req.set_msg_data(pstMsgRes->SerializeAsString());
    FindModule<NFIServerMessageModule>()->SendMsgToLogicServer(NF_ST_LOGIC_SERVER, m_targetSvrId, E_SS_ZONE_TRANSFER_MIRROR_SESSION, req, GetGlobalId());

    return 0;
}
